#coding: utf-8
import logging
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.escape
import os.path
import tornado.options
from tornado import database
import random
import string
from datetime import datetime
import re
import math
from trans2base64 import trans2base64
import hashlib

from tornado.options import define, options

define("port", default=8887, help="run on the given port", type=int)

def id_generator(size=3):
	return ''.join(random.choice(string.ascii_uppercase+string.digits) for x in range(size))

def filename_generator(fileExtName, size=3):
	filename = ''.join(random.choice(string.ascii_uppercase+string.digits) for x in range(size))
	while os.path.exists('./files/'+filename+'.'+fileExtName):
		filename = ''.join(random.choice(string.ascii_uppercase+string.digits) for x in range(size))
	return filename
	 	
		

def tzip(dt):
	arg = 2.0;
	return int(arg*math.log(dt/arg+1)*1000)
	# return int(dt*1000)

############################################################################
#                                 SESSIONS                                 #
#                   Control the message boardcasting                       #
############################################################################

class SessionDB:
	def __init__(self, db):
		self.sessions = {};
		self.db = db;
		self.liveSession = {};
		self.usedID = [];
	def new_session(self,type,title):
		sessionid = id_generator()
		while sessionid in self.usedID:
			sessionid = id_generator()
		self.usedID.append(sessionid)
		if type=="speech":
			session = SpeechSession(sessionid, title)
		elif type=="chat":
			session = ChatSession(sessionid, title)
		self.liveSession[sessionid] = session
		return session

	def find_session_dict(self,sessionid):
		if self.liveSession.has_key(sessionid):
			session = self.liveSession[sessionid]
			sessionDict = {"animation":session.get_animation_json(), "closed":session.closed, "title":session.title}
		else:
			try:
				sessionDict = self.db.query("SELECT * FROM `sketch`.`session` WHERE `sessionid`=%s", sessionid)[0]
				sessionDict["closed"] = True
				# logging.info(repr(session))
			except:
				sessionDict = None
		return sessionDict

	def find_session_obj(self,sessionid):
		if self.liveSession.has_key(sessionid):
			session = self.liveSession[sessionid]
		else:
			session = None
		return session

	def member_session(self,member):
		for session in self.liveSession.values():
			if member in session.members.keys():
				return session
		return None

	def write_down(self,session):
		try:
			self.liveSession[session.sessionid]
			if session.animation:
				self.db.execute("INSERT INTO `sketch`.`session` (`title`, `sessionid`, `animation`, `type`, `cover`) VALUES (%s, %s, %s, %s, %s);", session.title, session.sessionid, session.get_animation_json(), session.type, session.cover)
				logging.info(session.title)
		except:
			logging.error("write down error!", exc_info=True)


	

class Session:
	def __init__(self, sessionid, title):
		self.sessionid = sessionid
		self.title = title
		self.members = {}
		self.animation = []
		self.closed = False
		self.tp = datetime.now()
		self.memberCnt = 0
		self.cover = ""
	def add_member(self,new_member):
		self.boardcast(tornado.escape.json_encode({'c':'new_member', 'p':{'mid':self.memberCnt}}))
		self.members[new_member] = self.memberCnt
		new_member.write_message(tornado.escape.json_encode({'c':'set_memberID', 'p':{'mid':self.memberCnt}}))
		self.memberCnt += 1
	def remove_member(self,member):
		self.send(tornado.escape.json_encode({'c':'member_leave', 'p':{'mid':self.members[member]}}),member)
		try:
			del self.members[member]
		except:
			pass
	def send(self, message, sender):
		for receiver in self.members.keys():
			if receiver != sender:
				try:
					receiver.write_message(message)
				except:
					logging.error("Error sending message", exc_info=True)
		#recode
		tn = datetime.now()
		dt = tzip((tn-self.tp).seconds+(tn-self.tp).microseconds/1000000.0)
		self.tp = tn;
		self.animation.append([dt,message])
	def boardcast(self, message):
		self.send(message,None)
	def close_session(self):
		if not self.closed:
			self.boardcast(tornado.escape.json_encode({'c':'session_finish', 'p':{}}))
			sessionDB.write_down(self)
		self.closed = True
	def get_animation_json(self):
		past_animation = tornado.escape.json_encode(self.animation)
		past_animation = re.sub(r'"',r'\"', past_animation)
		return past_animation


class SpeechSession(Session):
	"""docstring for SpeechSession"""
	def __init__(self, sessionid, title):
		self.speaker = None
		self.type = "speech"
		Session.__init__(self,sessionid,title)
	def add_speaker(self, speaker):
		self.speaker = speaker
		self.add_member(speaker)
	def remove_member(self,member):
		Session.remove_member(self,member)
		if member == self.speaker:
			self.close_session()

class ChatSession(Session):
	"""docstring for ChatSession"""
	def __init__(self, sessionid,title):
		self.type = "chat"
		Session.__init__(self,sessionid,title)
	def remove_member(self,member):
		Session.remove_member(self,member)
		logging.info(repr(self.members))
		if self.members=={}:
			self.close_session()
	
############################################################################
#                          USERS DATABASE CLASS                            #
#                       operating users' information                       #
############################################################################

#FIXME


############################################################################
#                      PAGE and WEBSOCKET HANDLERS                         #
#                       tornado handler instants                           #
############################################################################

class BaseHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        return self.get_secure_cookie("user")
		
class SpeechPageHandler(tornado.web.RequestHandler):
	def get(self,sessionid):
		if self.current_user:
			user = tornado.escape.xhtml_escape(self.current_user)
		else:
			user = "游客"
		if not sessionid:
			nickname = self.request.arguments["nickname"][0]
			title = self.request.arguments["title"][0]
			session = sessionDB.new_session('speech',title)
			sessionid = session.sessionid
			past_animation = "[]"
			hasclosed = "false"
			self.render("pane.html", sessionid=sessionid, type="speech", past_animation=past_animation, hasclosed=hasclosed, user=user, title=title, nickname=nickname)
		else:
			sessionDict = sessionDB.find_session_dict(sessionid)
			if sessionDict:
				past_animation = sessionDict["animation"]
				title = sessionDict.title
				if sessionDict["closed"]:
					hasclosed = "true"
					self.render("WatchClosedPage.html", sessionid=sessionid, type="speech", past_animation=past_animation, hasclosed=hasclosed, title=title)
				else:
					nickname = user
					hasclosed = "false"
					self.render("WatchPage.html", sessionid=sessionid, type="speech", past_animation=past_animation, hasclosed=hasclosed, user=user, title=title, nickname=nickname)
			else:
				self.render("ErrorPage.html", title="出错了", message="找不到诶~", user=user)
		# self.write("hello,world")

class SpeechWebSocketHandler(tornado.websocket.WebSocketHandler):
	def allow_draft76(self):
		#for iOS 5.0 Safari
		return True

	def open(self,sessionid):
		session = sessionDB.find_session_obj(sessionid)
		if not session:
			logging.error("Invalid sessionid: %s", sessionid)
			self.close()
		else:
			if not session.speaker:
				logging.info("New session: %s",sessionid)
				session.add_speaker(self);
			else:
				logging.info("New receiver join to session %s", sessionid)
				session.add_member(self);

	def on_close(self):
		session = sessionDB.member_session(self)
		session.remove_member(self)
		logging.info("A member has quited")

	def on_message(self, message):
		session = sessionDB.member_session(self)
		sessionid = session.sessionid
		if session.speaker != self:
			logging.error("Invalid speaker %s", repr(self))
		else:
			logging.info("Session %s got message: %s", sessionid, message)
			session.send(message,self)

class ChatPageHandler(BaseHandler):
	def get(self,sessionid):
		if self.current_user:
			user = tornado.escape.xhtml_escape(self.current_user)
		else:
			user = "游客"
		if not sessionid:
			nickname = self.request.arguments["nickname"][0]
			title = self.request.arguments["title"][0]
			session = sessionDB.new_session('chat',title)
			sessionid = session.sessionid
			past_animation = "[]"
			hasclosed = "false"
			self.render("pane.html", sessionid=sessionid, type="chat", past_animation=past_animation, hasclosed=hasclosed, user=user, title=title, nickname=nickname)
		else:
			sessionDict = sessionDB.find_session_dict(sessionid)
			if sessionDict:
				past_animation = sessionDict["animation"]
				title = sessionDict["title"]
				if sessionDict["closed"]:
					hasclosed = "true"
					self.render("WatchClosedPage.html", sessionid=sessionid, type="chat", past_animation=past_animation, hasclosed=hasclosed, title=title)
				else:
					nickname = user
					hasclosed = "false"
					self.render("pane.html", sessionid=sessionid, type="chat", past_animation=past_animation, hasclosed=hasclosed, user=user, title=title, nickname=nickname)
			else:
				self.render("ErrorPage.html", title="出错了", message="找不到诶~" ,user=user)

		

class ChatWebSocketHandler(tornado.websocket.WebSocketHandler):
	def allow_draft76(self):
		#for iOS 5.0 Safari
		return True

	def open(self,sessionid):
		if not sessionDB.find_session_obj(sessionid):
			logging.error("Invalid sessionid: %s", sessionid)
			self.close()
		else:
			logging.info("New receiver join to session %s", sessionid)
			sessionDB.find_session_obj(sessionid).add_member(self);

	def on_close(self):
		session = sessionDB.member_session(self)
		session.remove_member(self)
		logging.info("A member has quited")

	def on_message(self, message):
		session = sessionDB.member_session(self)
		sessionid = session.sessionid
		logging.info("Session %s got message: %s", sessionid, message)
		session.send(message, self)

class UploadHandler(tornado.web.RequestHandler):
	"""docstring for UploadHandler"""
	def post(self):
		upload_file = self.request.files['upload'][0]
		sessionid = self.request.arguments['sessionid'][0]
		x = int(self.request.arguments['x'][0])
		y = int(self.request.arguments['y'][0])
		targetid = self.request.arguments['targetid'][0]
		session = sessionDB.find_session_obj(sessionid)
		fileExtName = upload_file['filename'].split('.')[-1]
		logging.info(fileExtName)
		fileName = filename_generator(fileExtName);
		logging.info(fileName)
		f = open('./files/'+fileName+'.'+fileExtName, 'w')
		f.write(upload_file['body'])
		f.close()
		base64_list = trans2base64(fileName+'.'+fileExtName)
		pageOffset = 0
		for page in base64_list:
			session.boardcast(tornado.escape.json_encode({'c':'insert_image', 'p':{'src':'data:image/jpeg;base64,'+page[0], 'x':x-page[1]/2 + pageOffset, 'y':y-page[2]/2, 'width':page[1], 'height':page[2], "targetid":targetid}}))
			targetid = targetid.split(':')[0]+':'+str(int(targetid.split(':')[1]))
			pageOffset+=page[1]+100


		# logging.info(base64_list)
		#FIXME

class HomePageHandler(BaseHandler):
	def get(self):
		if self.current_user:
			user = tornado.escape.xhtml_escape(self.current_user)
			# logging.info(repr(self.current_user))
		else:
			user = ""
		query = []
		try:
			query = db.query("SELECT * FROM  `session` WHERE  `cover` !=  ''ORDER BY  `datetime` DESC LIMIT 0 , 9")
		except:
			pass
		self.render("HomePage.html", user=user, newest_sessions=query);

class LoginPageHandler(tornado.web.RequestHandler):
	def post(self):
		username = self.request.arguments["username"][0]
		password = self.request.arguments["password"][0]
		# remember_me = self.request.arguments["remember_me"][0] == "on"
		pw_md5 = hashlib.md5()
		pw_md5.update(password)
		try:
			query = db.get("SELECT * FROM `sketch`.`user` WHERE `email`=%s and `pw`=%s", username, pw_md5.hexdigest())
			# self.write(query["name"])
			self.set_secure_cookie('user',query["email"])
			# self.set_secure_cookie('nickname',query["name"])
			self.redirect("/")
		except:
			self.render("ErrorPage.html", title="出错了", message="诶~")


class RegisterPageHandler(BaseHandler):
	def post(self):
		email = self.request.arguments["username"][0]
		password = self.request.arguments["password"][0]
		password_again = self.request.arguments["password_again"][0]
		nickname = ''
		logging.info(password)
		# if re.match(r'.+@.+', email):
		# 	self.render("ErrorPage.html", title="出错了", message="这好像不是邮箱地址~") #FIXME
		try:
			if db.get("SELECT * FROM `sketch`.`user` WHERE `email`=%s", email):
				self.render("ErrorPage.html", title="出错了", message="这个邮箱已经注册过了~")
			elif (password == password_again) and (re.match(r'[A-Za-z0-9@#$%^&+=\(\)]{8,}', password)):
				pw_md5 = hashlib.md5()
				pw_md5.update(password)
				try:
					db.execute("INSERT INTO `sketch`.`user` (`name`, `email`, `pw`) VALUES (%s, %s, %s);", nickname, email, pw_md5.hexdigest())
				except:
					self.render("ErrorPage.html", title="出错了", message="你的注册信息好像有问题诶~")
				self.write(nickname+':'+email+':'+password)
				self.set_secure_cookie('user',email);
				# self.set_secure_cookie('nickname',nickname)
				self.redirect("/")
			else:
				self.render("ErrorPage.html", title="出错了", message="你的注册信息好像有问题诶~")
		except:
				self.render("ErrorPage.html", title="出错了", message="你的注册信息好像有问题诶~")


class SharePageHandler(BaseHandler):
	def post(self):
		if self.current_user:
			user = tornado.escape.xhtml_escape(self.current_user)
		else:
			user = ""
		sType = self.request.arguments["type"][0]
		sessionid = self.request.arguments["sessionid"][0]
		cover = self.request.arguments["cover"][0]
		title = self.request.arguments["title"][0]
		self.render("SharePage.html", type=sType, sessionid=sessionid, cover=cover, user=user, title=title)

class UpdateCoverPageHandler(BaseHandler):
	def post(self):
		cover = self.request.arguments["cover"][0]
		sessionid = self.request.arguments["sid"][0]
		sessionDB.find_session_obj(sessionid).cover = cover;


class Application(tornado.web.Application):
	"""Main Application"""
	def __init__(self):
		handlers = [
			(r"/?", HomePageHandler),
			(r"/login/?", LoginPageHandler),
			(r"/register/?", RegisterPageHandler),
			(r"/share/?", SharePageHandler),
			(r"/update_cover/?", UpdateCoverPageHandler),
			(r"/speech/?(.*)", SpeechPageHandler),
			(r"/socket/speech/(.*)", SpeechWebSocketHandler),
			(r"/chat/?(.*)", ChatPageHandler),
			(r"/socket/chat/(.*)", ChatWebSocketHandler),
			(r"/upload/", UploadHandler),
		]
		settings = dict(
		    cookie_secret="43oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/VL=",
		    template_path=os.path.join(os.path.dirname(__file__), "templates"),
		    static_path=os.path.join(os.path.dirname(__file__), "static"),
		    # xsrf_cookies=True,
		    autoescape=None,
		)
		tornado.web.Application.__init__(self, handlers, **settings)


def main():
    tornado.options.parse_command_line()
    global db
    # db = None
    db = database.Connection("localhost", "sketch", user="sketch", password="19920119")
    global sessionDB
    sessionDB = SessionDB(db)
    app = Application()
    app.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
    main()
		


			
